﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;

namespace VGS {
    /// <summary>
    /// This class holds a collection of unknown number of 'Set's.
    /// This class is also enumerable - possible to enumerate over each Set.
    /// </summary>
    public class Sets : IList<Set> {
        // List containing the sets
        private List<Set> sets = new List<Set>();
        // Index of the current set
        private int _current = -1;

        #region Properties
        /// <summary>
        /// Gets the total number of set wins for the home team
        /// </summary>
        public int HomeTeamSetWins {
            get {
                int count = 0;
                foreach (Set s in sets) {
                    if (s.Winner == TeamEnum.HOME_TEAM)
                        count++;
                }
                return count;
            }
        }


        /// <summary>
        /// Gets the total number of set wins for the away team
        /// </summary>
        public int AwayTeamSetWins {
            get {
                int count = 0;
                foreach (Set s in sets) {
                    if (s.Winner == TeamEnum.AWAY_TEAM)
                        count++;
                }
                return count;
            }
        }

        
        /// <summary>
        /// Gets or sets the current set
        /// </summary>
        /// <exception cref="InvalidOperationException">Invalid set</exception>
        public Set CurrentSet
        {
            get
            {
                if (_current < 0 || _current >= sets.Count)
                    throw new InvalidOperationException("Invalid set");
                return sets[_current];
            }
        }
        #endregion

        #region IList<>
        /// <summary>
        /// Searches the specified object and returns the zero-based index of the first
        /// occurance within the list.
        /// </summary>
        /// <param name="item">Set item to search for</param>
        /// <returns>Set item's index</returns>
        public int IndexOf(Set item)
        {
            return sets.IndexOf(item);
        }

        /// <summary>
        /// Inserts an item into the list at the specified index.
        /// </summary>
        /// <param name="index">Index to insert at</param>
        /// <param name="item">Item to insert</param>
        public void Insert(int index, Set item)
        {
            sets.Insert(index, item);
        }

        /// <summary>
        /// Gets or sets the item at the specified zero-based index.
        /// </summary>
        /// <param name="index">Zero-based index</param>
        /// <returns>The set at the specified index</returns>
        public Set this[int index]
        {
            get
            {
                if (index < 0 || index >= sets.Count)
                    throw new ArgumentOutOfRangeException("index", "Index out of range");
                return sets[index];
            }
            set
            {
                if (index < 0 || index >= sets.Count)
                    throw new ArgumentOutOfRangeException("index", "Index out of range");
                sets[index] = value;
            }

        }

        /// <summary>
        /// Removes the item at the specified zero-based index.
        /// </summary>
        /// <param name="index">The zero-based index</param>
        public void RemoveAt(int index)
        {
            if (index < 0 || index >= sets.Count)
                throw new ArgumentOutOfRangeException("index", "Index out of range");
            sets.RemoveAt(index);
        }
        #endregion

        #region ICollection<>
        /// <summary>
        /// Gets the total number of sets.
        /// </summary>
        public int Count
        {
            get
            {
                return sets.Count;
            }
        }


        /// <summary>
        /// Removes all elements from the list.
        /// </summary>
        public void Clear()
        {
            sets.Clear();
        }

        /// <summary>
        /// Determines whether an element is in the list.
        /// </summary>
        /// <param name="item">Item to search for</param>
        /// <returns>true if the specified element is in the list, otherwise false</returns>
        public bool Contains(Set item)
        {
            return sets.Contains(item);
        }

        /// <summary>
        /// Copies the sets list to the specified array, starting at arrayIndex.
        /// </summary>
        /// <param name="array">Array to copy to</param>
        /// <param name="arrayIndex">The source's start index</param>
        public void CopyTo(Set[] array, int arrayIndex)
        {
            try
            {
                sets.CopyTo(array, arrayIndex);
            }
            catch (Exception)
            {
                throw; // Rethrow same exception
            }
        }

        /// <summary>
        /// Add a new set
        /// </summary>
        /// <param name="item"></param>
        public void Add(Set item)
        {
            // If it is the first set we add
            if (_current == -1) _current++;

            sets.Add(item);
        }

        /// <summary>
        /// Removes the first occurance of the specified object from the list.
        /// </summary>
        /// <param name="item">Item to be removed</param>
        /// <returns>true if an item was removed, otherwise false</returns>
        public bool Remove(Set item)
        {
            return sets.Remove(item);
        }

        /// <summary>
        /// Since the list isn't read only, false is hardcoded to be returned.
        /// </summary>
        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }
        #endregion

        #region IEnumerable<>
        /// <summary>
        /// Return the generic enumerator.
        /// </summary>
        /// <returns>The generic IEnumerator</returns>
        public IEnumerator<Set> GetEnumerator()
        {
            foreach (Set s in sets)
                yield return s;
        }
        #endregion

        #region IEnumerable
        /// <summary>
        /// Hide the non-generic implementation.
        /// </summary>
        /// <returns>The generic implementation</returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
        #endregion

        #region Methods
        /// <summary>
        /// We want to ensure that there always is at least *one* set.
        /// Since sets gets their number indifier when they are created,
        /// we don't allow removing of sets. However, a set can be reseted.
        /// </summary>
        public Sets()
        {
            Add(new Set(sets.Count + 1));
        }


        /// <summary>
        /// This method steps ahead to the next set. It also unsubscribes all methods to the events
        /// of the previous set
        /// </summary>
        /// <returns>true if there was a next set that it could increment to,
        /// otherwise false</returns>
        public bool Next() 
        {
            if (_current < sets.Count - 1) // If there is a next set
            {
                // Remove all listener to the events. It is possible to assign
                // null since I left out the 'event' keyword.
                sets[_current].Lineups.HomeTeam.PlayerAdded = null;
                sets[_current].Lineups.HomeTeam.PlayerRemoved = null;
                sets[_current].Lineups.HomeTeam.PlayersRotated = null;
                sets[_current].Lineups.AwayTeam.PlayerAdded = null;
                sets[_current].Lineups.AwayTeam.PlayerRemoved = null;
                sets[_current].Lineups.AwayTeam.PlayersRotated = null;
                if (sets[_current].Status != SetStatus.SET_WARMUP)
                    sets[_current].Points.ScoreChanged = null;
                sets[_current].SetReseted = null;

                // Go to the next set
                _current++;
                return true;
            }
            return false;
        }

        /// <summary>
        /// This method goes to the previous set. The method also unsubscibe from all
        /// events of the "old" set.
        /// </summary>
        /// <returns>true if there is a set previous to the current one,
        /// otherwise false</returns>
        public bool Previous()
        {
            if (_current > 0) // If there is a previous set
            {
                // Remove all associated events for the current set
                sets[_current].Lineups.HomeTeam.PlayerAdded = null;
                sets[_current].Lineups.HomeTeam.PlayerRemoved = null;
                sets[_current].Lineups.HomeTeam.PlayersRotated = null;
                sets[_current].Lineups.AwayTeam.PlayerAdded = null;
                sets[_current].Lineups.AwayTeam.PlayerRemoved = null;
                sets[_current].Lineups.AwayTeam.PlayersRotated = null;
                if (sets[_current].Status != SetStatus.SET_WARMUP)
                    sets[_current].Points.ScoreChanged = null;
                sets[_current].SetReseted = null;

                _current--;
                return true;
            }
            return false;
        }
        #endregion
    }
}
